package com.skyable.account.aop;

import com.skyable.account.config.CloudApplicationContext;
import com.skyable.common.common.ResponseResult;
import com.skyable.common.constants.exception.CommonExcpConstants;
import com.skyable.common.constants.param.CommonParams;
import com.skyable.common.exceptions.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * 类功能描述。
 * @Filename: WebLogAcpect.java
 * @Description:
 * @Version: 1.0
 * @Author: GaoYuan
 * @History: Author: GaoYuan/Date: 2020年11月07日/Version:1.0/Content: create
 */
@Aspect
@Component
@Slf4j
public class WebLogAcpect {

    /**
     * 定义切入点，切入点为com.skyable.device.controller下的所有函数
     */
    @Pointcut("execution(public * com.skyable.account.controller..*.*(..))")
    public void webLog(){}

    /**
     * 前置通知：在连接点之前执行的通知
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String methodName = joinPoint.getSignature().getName();
        //设置ThreadLocal值
        String domainId = request.getHeader(CommonParams.HEADER_USER_DOMAIN_ID);
        CloudApplicationContext.domainId.set(Long.valueOf(domainId));
        // 记录下请求内容
        log.info("method["+methodName+"]request path: " + request.getRequestURL().toString());
        log.info("method["+methodName+"]request method: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("method["+methodName+"]request param: " + Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 后置通知
     * @param joinPoint
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret",pointcut = "webLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        // 处理完请求，返回内容
        log.info("method["+methodName+"]return content: " + ret);
    }

    /**
     * 环绕通知：
     *   环绕通知非常强大，可以决定目标method是否执行，什么时候执行，执行时是否需要替换method参数，执行完毕是否需要替换返回值。
     *   环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     */
    @Around(value = "webLog()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        String methodName = proceedingJoinPoint.getSignature().getName();
        try {
            long startTime = System.currentTimeMillis();
            Object obj = proceedingJoinPoint.proceed();
            long endTime = System.currentTimeMillis();
            log.info("method["+methodName+"]deal tine: " + (endTime - startTime) + " ms");
            return obj;
        } catch (Throwable throwable) {
            log.error("method[" + methodName + "] occur error :", throwable);
            //throw new BusinessException(throwable.getMessage(), 500);
            return ResponseResult.error(CommonExcpConstants.ERROR_CODE, CommonExcpConstants.ERROR_MSG);
        }
    }

    /**
     * 该注解标注的method在所有的Advice执行完成后执行，无论业务模块是否抛出异常，类似于finally的作用；
     * @param joinPoint
     */
    @After(value="webLog()")
    public void logEnd(JoinPoint joinPoint){
        //移除ThreadLocal值
        CloudApplicationContext.domainId.remove();
    }

}
